<template>
  <a-button
    type="primary"
    :disabled="!$auth('upload:chunk')"
    @click="visible = true"
  >
    切片上传
  </a-button>

  <DraggableModal
    v-model:open="visible"
    title="上传"
    :width="800"
    ok-text="开始上传"
    :ok-button-props="{ disabled: !isCanUpload }"
    @ok="onOk"
    @cancel="onCancel"
  >
    <a-alert
      class="tip-style"
      message="实现文件的切片上传"
      type="info"
      show-icon
    />
    <Upload
      v-model:file-list="fileList"
      :maxCount="1"
      :before-upload="beforeUpload"
      @change="handleChange"
      @onRemove="handleRemove"
    >
      <div class="upload-body">
        <Icon class="upload-icon" icon="ant-design:upload-outlined" />
        <div class="ant-upload-text">点击或者拖拽上传</div>
      </div>
    </Upload>
    <!-- 上传进度展示 -->
    <Progress
      class="process-body"
      v-if="fileList.length > 0"
      :percent="uploadPercent"
      size="small"
      status="normal"
    />
  </DraggableModal>
</template>

<script setup lang="tsx">
import { ref, computed } from 'vue';
import {
  message,
  Upload,
  type UploadProps,
  type UploadFile,
} from 'ant-design-vue';
import { DraggableModal } from '@/components/core/draggable-modal';
import Api from '@/api/';
import SparkMd5 from 'spark-md5';

const emit = defineEmits(['uploadSuccess']);

const visible = ref(false);
const fileList = ref<UploadFile[]>([]);
const isCanUpload = computed(() => {
  return fileList.value.length > 0 && uploadCount.value <= 0;
});
const CHUNK_SIZE = 1024 * 1024 * 1; //5M
const fileChunks = ref<File[]>([]);
const fileHash = ref('');
const fileName = ref('');
const fileSize = ref(0);
//上传的进度和状态
const isCancelUpload = ref(true);
const uploadCount = ref(0);
const uploadPercent = computed(() => {
  let result = 100;
  if (fileList.value.length > 0) {
    result = Math.floor((uploadCount.value / fileChunks.value.length) * 100);
  }
  return result;
});

const beforeUpload: UploadProps['beforeUpload'] = async file => {
  uploadCount.value = 0;
  console.log(file);
  fileList.value.push(file);

  fileSize.value = file.size;
  fileName.value = file.name;

  //文件分片
  const chunks = createChunks(file);
  fileChunks.value = chunks;
  //哈希值计算
  fileHash.value = await calculateHash(chunks);
  return false;
};

const handleChange: UploadProps['onChange'] = ({ file }) => {
  file.status = 'done';
};

const handleRemove: UploadProps['onRemove'] = async file => {
  fileList.value = [];
};

const onCancel = () => {
  fileList.value = [];
  visible.value = false;
  uploadCount.value = 0;
  isCancelUpload.value = true;
};

const onOk = async () => {
  isCancelUpload.value = false;
  let chunkSignsArr = new Array(fileChunks.value.length).fill(0);
  /* 判断是否已有切片，有切片 (断点续传)，有完整文件 (秒传) */
  const data = await Api.toolsUpload.uploadCheckChunks({
    fileName: fileName.value,
    fileHash: fileHash.value,
    chunkTotal: fileChunks.value.length,
  });
  if (data.uploadStatus === 'uploaded') {
    message.success('上传成功');
    onCancel();
    return;
  } else if (data.uploadStatus === 'uploading') {
    chunkSignsArr = [...data.chunkSignsArr];
  }

  console.log('开始上传分片');
  //上传分片
  await uploadChunks(chunkSignsArr, fileChunks.value);
};

//文件分片操作
const createChunks = (file: File) => {
  let cur = 0;
  let chunks: any = [];
  while (cur < file.size) {
    // 修复：确保最后一个分片不会超出文件大小
    const end = Math.min(cur + CHUNK_SIZE, file.size);
    chunks.push(file.slice(cur, end));
    cur = end; // 使用实际结束位置递增，避免累计误差
  }
  return chunks;
};

//计算哈希值,进行增量计算
const calculateHash = async (chunks: Blob[]) => {
  return new Promise<string>((resolve, reject) => {
    const spark = new SparkMd5.ArrayBuffer();
    const reader = new FileReader();
    let index = 0;

    // 优化：使用单个reader复用，减少内存占用
    reader.onload = function (e) {
      if (!e.target?.result) {
        reject(new Error('读取分片失败'));
        return;
      }

      spark.append(e.target.result as ArrayBuffer);
      index++;

      if (index < chunks.length) {
        // 继续读取下一个分片
        loadNextChunk();
      } else {
        // 所有分片处理完成
        resolve(spark.end());
      }
    };

    reader.onerror = function () {
      reject(new Error('文件读取错误: ' + reader.error?.message));
    };

    // 加载下一个分片的函数
    function loadNextChunk() {
      reader.readAsArrayBuffer(chunks[index]);
    }

    // 开始读取第一个分片
    loadNextChunk();
  });
};

//上传分片
const uploadChunks = async (chunkSignsArr, chunks: Blob[]) => {
  const max = 6;
  let index = 0;
  const taskPool: any = [];
  while (index < chunks.length) {
    if (isCancelUpload.value) {
      return;
    }
    if (chunkSignsArr[index] === 0) {
      const task = await Api.toolsUpload.uploadUploadChunk(
        {
          chunk: chunks[index],
          fileHash: fileHash.value,
          chunkHash: fileHash.value + '-' + index,
        },
        chunks[index] as File
      );

      taskPool.splice(taskPool.findIndex((item: any) => item === task));
      taskPool.push(task);
      if (taskPool.length === max) {
        await Promise.race(taskPool);
      }
    }
    index++;
    uploadCount.value = index;
  }
  if (isCancelUpload.value) {
    return;
  }
  await Promise.all(taskPool);
  const res = await Api.toolsUpload.uploadUploadChunkMerge(
    {
      fileHash: fileHash.value,
      fileName: fileName.value,
      fileSize: fileSize.value,
    },
    { isReturnResult: false }
  );
  if (res?.success) {
    message.success('上传成功');
    onCancel();
    emit('uploadSuccess');
  } else {
    message.error('上传失败,请重试');
  }
};
</script>

<style scoped lang="less">
.tip-style {
  margin-bottom: 10px;
}
.upload-body {
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  width: 100%;
  height: 100px;
  margin: 20px 0px;
  padding: 20px 0px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  overflow: hidden;
}
:deep(.ant-upload-select) {
  display: block !important;
}
.process-body {
  width: 100%;
  padding: 0px 10px;
  margin: 0px;
  overflow: hidden;
}
</style>
